/*
 * Copyright 2007-2010 Jeremy Brown.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author <a href="http://jeremy-brown.com">Jeremy Brown</a>
 */


package groovy.prefuse.factory

import prefuse.data.Graph
import prefuse.data.Tree
import prefuse.data.Node as PrefuseNode //so we don't conflict with groovy.util.Node

public class NodeFactory extends AbstractFactory
{
	boolean isLeaf()
	{
		return false
	}
    
    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException
	{
		def parent = builder.getCurrent() //get this node's parent node - getParentNode() returns grandparent node when used in the newInstance(...) method
		def parentGraph

		if(parent instanceof Graph)
		{
			parentGraph = parent
		}
		else if(parent instanceof PrefuseNode)
		{
			parentGraph = parent.getGraph()
		}

		if(parent instanceof Tree)
		{
			//source and target are not allowd for Tree
			attributes.remove("source")
			attributes.remove("target")
		}
		else
		{
			//source and target can be used to specify directionality of edges in a Graph
			//Take them out here, and use them when creating the Edge between nodes in NodeFactory.onNodeCompleted(...)
			builder.context.source = attributes.remove("source")
			builder.context.target = attributes.remove("target")
		}

		
		PrefuseNode node = parentGraph.addNode()
		
		def values = []
		
		if(value instanceof List)
		{
			//Accept data in a list form. The order of the data in the list should correspond to the columns of data specified for the graph/tree with columnNames:[...]
			//e.g.:
			//pfGraph(id:"graph", columnNames:["product", "city", "price"], columnTypes:[String,String,Integer])
			//{
			//	pfNode(["Pita bread (20)", "Brazzaville", 2500])
			//	{
			//		pfNode(["Coca-cola", "Brazzaville", 800])
			//	}
			//}
			values = value
		}
		else if((attributes.size() > 0 && attributes.id == null) || attributes.size() > 1)
		{
			//Don't accept input like pfNode("my data", id:"someID") here. Force them to be processed below with values = [value].
			//Here, accept input like pfNode(product:"Pita bread (20)", city:"Brazzaville", price:2500, id:"pita"). This will load product, city, and price values into the respective fields. id is ignored here and will be processed by PrefuseBuilder.objectIDAttributeDelegate(...).
			values = [:]
			attributes.each{key, val->
				//don't consume the id attribute
				if(key != "id")
				{
					values.put(key, val)
				}
			}
			values.each{key, val->
				attributes.remove(key)
			}
		}
		else if(value != null)
		{
			//Accept input like:
			//pfNode("my data", id:"someID")
			//pfNode("my data")
			values = [value]
		}
		
		
		if(values instanceof List)
		{
			//In List mode, assign the data columns to the node in the order they were created for the tree/graph with the columnNames attribute
			values.eachWithIndex{data, columnIndex->
				node.set(columnIndex, data)
			}
		}
		else if(values instanceof Map)
		{
			//In Map mode, assign the data columns to the node by their key
			values.each{columnName, data->
				node.set(columnName, data)
			}
		}
		
		return node
	}
	
	
	public void onNodeCompleted(FactoryBuilderSupport builder, Object parent, Object child)
	{
		//setParent() in this case is used to set Edges between Nodes.
		if(parent instanceof PrefuseNode && child instanceof PrefuseNode)
		{
			def graph = child.getGraph()
			
			//source and target were pulled out in NodeFactory.newInstance(...)
			
			if(builder.context.source && !builder.context.target)
			{
				graph.addEdge(child, parent)
			}
			else if(builder.context.target && !builder.context.source)
			{
				graph.addEdge(parent, child)
			}
			else
			{
				graph.addEdge(parent, child)
			}
		}
	}
}
